package com.moro.lisp.typedefine;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.moro.lisp.JLispData;
import com.moro.lisp.JLispMacroName;
import com.moro.lisp.JLispType;
import com.moro.lisp.tools.FunctionPool;
import com.moro.lisp.tools.MacroPool;
import com.moro.lisp.tools.SymbolPool;
import com.moro.lisp.typedefine.Error;

public class Macro {
	
	
	/*
	 * 杩欎釜瀛楁琛ㄧず杩欎釜瀹忕殑鍚嶅瓧锛岋紙瀹忓悕瀛楅�氬父鎰忎箟涓嶅ぇ锛屼粎鏄负浜嗘爣璁板畯锛屽洜涓哄畯鏃犳硶閫氳繃瀹忓悕瀛楄浣跨敤锛�
	 */
	public JLispMacroName macroName;
	
	
	
	
	/*
	 * 杩欎釜瀛楁琛ㄧず鍙傛暟鐨勪釜鏁版槸涓嶆槸鍥哄畾鐨勶紝涓簍rue琛ㄧず鏄紝涓篺alse琛ㄧず涓嶆槸
	 */
	public boolean isFixedArgs;
	
	/*
	 * 杩欎釜鍙傛暟琛ㄧず杩欎釜瀹忕殑鍙傛暟鍒楄〃锛屽洜涓哄弬鏁板垪琛ㄥ彧鑳芥槸鍒楄〃锛屼笉鍙兘鏄叾瀹冩暟鎹被鍨�	
	 */
	public Cons args;
	
	
	
	/*
	 * 杩欎釜瀛楁琛ㄧず杩欎釜瀹忓嚱鏁扮殑闂寘锛岄棴鍖呬腑鍙兘鏄師瀛�	
	 */
	public List<Atom> closure;
	
	
	/*
	 * 杩欎釜鍙傛暟琛ㄧず杩欎釜瀹忕殑鍙傛暟鍒楄〃锛屽彲浠ヤ负鍘熷瓙nil鐨勬儏鍐�	
	 */
	public Atom nil;
	
	
	/*
	 * 杩欎釜鍙傛暟琛ㄧず瀹忕殑瀹忎綋锛岀敱涓�涓垨澶氫釜JavaLisp鏁版嵁缁勬垚锛屾墍浠ョ敤List闆嗗悎鏉ュ瓨鍌紝涓轰粈涔堜笉鐢–ons锛屽洜涓哄畯浣撳彲鑳芥槸鍒楄〃锛屼篃鍙兘鏄叾瀹冪被鍨嬬殑鏁版嵁
	 */
	private List<JLispData> macroBodys;
	
	/*
	 * 杩欎釜瀛楁琛ㄧず璇ュ畯浣滀负鍝簺鍘熷瓙鐨勫嚱鏁板�硷紙瀹忎笉鑳戒綔涓哄師瀛愮殑涓�鑸�硷級锛岀敤List闆嗗悎瑁呰捣鏉ワ紝褰撲笉浣滀负浠讳綍鍘熷瓙鐨勫嚱鏁板�肩殑鏃跺�欙紝鍒欒瀹忓皢浠庣粓绔畯姹犱腑琚Щ闄わ紝锛堝洜涓烘棤娉曡寮曠敤锛岄偅杩欎釜瀹�
	 * 灏嗘病鏈夊瓨鍦ㄧ殑鎰忎箟锛�
	 */
	public List<Atom> asSymbolFunction;
	
	
	public Macro(JLispMacroName macroName,Atom atom,Cons args,List<JLispData> macroBodys, List<Atom> closure) {
		this.macroName = macroName;
		
		this.args = args;
		this.nil = null;
		this.isFixedArgs = isFixedArgs(this.args);
		this.macroBodys = macroBodys;

		this.asSymbolFunction = new ArrayList<Atom>();
		if(null == closure) {
			this.closure = null;
		}else {
			this.closure = closure;
			/*for(Atom a:this.closure) {
				a.symbolValues.bind(new JLispData(this, JLispType.FUNCTION));
			}*/
		}
		atom.setFunctionVal(new JLispData(this,JLispType.MACRO));//灏嗚鍘熷瓙鐨勫嚱鏁板�艰涓烘鍑芥暟
	}
	
	public Macro(JLispMacroName macroName,Atom atom,Atom nil,List<JLispData> macroBodys, List<Atom> closure) {
		this.macroName = macroName;
		
		this.args = null;
		this.nil = nil;
		this.isFixedArgs = isFixedArgs(this.args);
		this.macroBodys = macroBodys;

		this.asSymbolFunction = new ArrayList<Atom>();
		if(null == closure) {
			this.closure = null;
		}else {
			this.closure = closure;
			/*for(Atom a:this.closure) {
				a.symbolValues.bind(new JLispData(this, JLispType.FUNCTION));
			}*/
		}
		atom.setFunctionVal(new JLispData(this,JLispType.MACRO));//灏嗚鍘熷瓙鐨勫嚱鏁板�艰涓烘鍑芥暟
	}
	private boolean isFixedArgs (Cons args) {
		return false;
	}
	
	
	public JLispData execute(JLispData[] args, SymbolPool symbolpool, FunctionPool functionPool, MacroPool macroPool) {//瀹忕殑鎵ц闇�瑕佺鍙锋睜锛堝彧鎵ц鑷畾涔夊畯锛屽叾瀹冨唴缃畯鐢变笂涓�灞傚畬鎴愶級
		if(JLispMacroName.CUSTOMIZE != this.macroName) {
			return new JLispData(new Error("Unknown Error!"), JLispType.ERROR);//濡傛灉涓嶆槸鑷畾涔夊畯杩斿洖涓�涓湭鐭ラ敊璇紝鑲畾鏄笂灞傝皟鐢ㄥ嚭浜嗛棶棰�
		}
		
		Map<String, JLispData> closureMap = findClosures(args);
		
		if(null == this.args && symbolpool.getSymbolByString("nil") == this.nil) {//濡傛灉鍙傛暟鍒楄〃涓虹┖鍒楄〃
			if(args != null && args.length > 0) {//濡傛灉鏈夊弬鏁版暟缁勪紶杩涙潵鐨勮瘽锛屽垯浜х敓涓�涓紓甯革紝浣嗕笉杩斿洖
				new Error("The customize macro expected 0 args");
			}
			if(null == this.macroBodys || this.macroBodys.size() == 0) {//濡傛灉娌℃湁鍑芥暟浣撳垯鐩存帴杩斿洖鍘熷瓙鈥渘il鈥�
				return new JLispData(symbolpool.getSymbolByString("nil"), JLispType.ATOM);
			}
			//鐩存帴杩愯鍑芥暟浣擄紙鑰冭檻闂寘锛�
			//闂寘鍏ユ爤
			closurePushStack();
			JLispData returnValue = null;
			for(JLispData body:this.macroBodys) {
				returnValue = body.eval(symbolpool, functionPool, macroPool);
				if(JLispType.ERROR == returnValue.type) {
					//瑙ｅ喅瀹忓嚱鏁板洜涓哄畯鍑芥暟浣撳彂鐢熼敊璇�屾彁鍓嶉��鍑烘椂闂寘鎴栧弬鏁版病鏈夊嚭鏍堢殑bug
					closurePopStack();
					//
					if(closureMap.size() > 0) {
						if(JLispType.ATOM == returnValue.type) {
							if(null != closureMap.get(((Atom)(returnValue.data)).name)) {
								return closureMap.get(((Atom)(returnValue.data)).name);
							}
						}else if(JLispType.CONS == returnValue.type) {
							replaceBack((Cons)(returnValue.data), closureMap);
						}
					}
					return returnValue;
				}
			}
			closurePopStack();
			//闂寘鍑烘爤
			
			if(closureMap.size() > 0) {
				if(JLispType.ATOM == returnValue.type) {
					if(null != closureMap.get(((Atom)(returnValue.data)).name)) {
						return closureMap.get(((Atom)(returnValue.data)).name);
					}
				}else if(JLispType.CONS == returnValue.type) {
					replaceBack((Cons)(returnValue.data), closureMap);
				}
			}
			return returnValue;
		}
		
		if(null == this.macroBodys || this.macroBodys.size() == 0) {//濡傛灉娌℃湁鍑芥暟浣撳垯鐩存帴杩斿洖鍘熷瓙鈥渘il鈥�
			return new JLispData(symbolpool.getSymbolByString("nil"), JLispType.ATOM);
		}
		closurePushStack();
		pushValuesToArgs(args, symbolpool);//姣忚繍琛屼竴娆¤嚜瀹氫箟鍑芥暟锛岄兘瑕佸皢鍙傛暟鍒楄〃涓殑绗﹀彿鍏ユ爤涓�娆�
		JLispData returnValue = null;
		for(JLispData body:this.macroBodys) {
			returnValue = body.eval(symbolpool, functionPool, macroPool);
			if(JLispType.ERROR == returnValue.type) {
				//瑙ｅ喅鍑芥暟鍥犱负鍑芥暟浣撳彂鐢熼敊璇�屾彁鍓嶉��鍑烘椂闂寘鎴栧弬鏁版病鏈夊嚭鏍堢殑bug
				closurePopStack();
				popValuesFromArgs(symbolpool);//濮ｅ繗绻嶇悰灞肩濞喡ゅ殰鐎规矮绠熼崙鑺ユ殶閿涘矂鍏樼憰浣哥殺閸欏倹鏆熼崚妤勩�冩稉顓犳畱缁楋箑褰块崙鐑樼垽娑擄拷濞嗭拷
				//
				
				if(closureMap.size() > 0) {
					if(JLispType.ATOM == returnValue.type) {
						if(null != closureMap.get(((Atom)(returnValue.data)).name)) {
							return closureMap.get(((Atom)(returnValue.data)).name);
						}
					}else if(JLispType.CONS == returnValue.type) {
						replaceBack((Cons)(returnValue.data), closureMap);
					}
				}
				return returnValue;
			}
		}
		popValuesFromArgs(symbolpool);//姣忚繍琛屼竴娆¤嚜瀹氫箟鍑芥暟锛岄兘瑕佸皢鍙傛暟鍒楄〃涓殑绗﹀彿鍑烘爤涓�娆�
		closurePopStack();
		
		if(closureMap.size() > 0) {
			if(JLispType.ATOM == returnValue.type) {
				if(null != closureMap.get(((Atom)(returnValue.data)).name)) {
					return closureMap.get(((Atom)(returnValue.data)).name);
				}
			}else if(JLispType.CONS == returnValue.type) {
				replaceBack((Cons)(returnValue.data), closureMap);
			}
		}
		return returnValue;
	}
	
	private Map<String, JLispData> findClosures(JLispData[] args) {
		Map<String, JLispData> closureMap = new HashMap<String, JLispData>();
		for(JLispData arg:args) {
			if(JLispType.ATOM == arg.type) {
				Atom atom = (Atom)(arg.data);
				if(null != atom.isClosureAtom) {
					closureMap.put(atom.name, arg);
				}
			}else if(JLispType.CONS == arg.type) {
				Cons cons = (Cons)(arg.data);
				findClosuresFromCons(cons, closureMap);
			}
		}
		return closureMap;
	}
	
	private void findClosuresFromCons(Cons cons, Map<String, JLispData> closureMap) {
		for(int i = 0;i < cons.getLength();i++) {
			JLispData item = cons.getItemById(i);
			if(JLispType.ATOM == item.type) {
				Atom atom = (Atom)(item.data);
				if(null != atom.isClosureAtom) {
					closureMap.put(atom.name, item);
				}
			}else if(JLispType.CONS == item.type) {
				Cons consOfCons = (Cons)(item.data);
				findClosuresFromCons(consOfCons, closureMap);
			}
		}
	}
	
	private void replaceBack(Cons cons, Map<String, JLispData> closureMap) {
		for(int i = 0;i < cons.getLength();i++) {
			JLispData item = cons.getItemById(i);
			if(JLispType.ATOM == item.type) {
				if(null != closureMap.get(((Atom)(item.data)).name)) {
					cons.setItemById(i, closureMap.get(((Atom)(item.data)).name));
				}
			}else if(JLispType.CONS == item.type) {
				replaceBack((Cons)(item.data), closureMap);
			}
		}
	}
	
	private void closurePushStack() {
		if(null == this.closure || this.closure.size() == 0) {
			return;
		}
		for(Atom closureAtom:this.closure) {
			closureAtom.setValue(closureAtom.isClosureAtom.value);
		}
	}
	private void closurePopStack() {
		if(null == this.closure || this.closure.size() == 0) {
			return;
		}
		for(Atom closureAtom:this.closure) {
			closureAtom.pop();
		}
	}
	private void pushValuesToArgs(JLispData[] args, SymbolPool symbolpool) {
		
		/*
		 * 棣栧厛妫�鏌ュ弬鏁板垪琛ㄤ腑鏈夋病鏈� &rest 鍘熷瓙
		 */
		if(this.args.indexOfAtom(symbolpool.getSymbolByString("&rest")) == -1) {//鍗充唬琛ㄥ弬鏁板垪琛ㄤ腑娌℃湁&rest鍘熷瓙
			/*
			 * 濡傛灉鍙傛暟鍒楄〃鐨勯暱搴﹀拰瀹為檯浼犺繘鏉ョ殑鍙傛暟鏁扮粍鐨勯暱搴︿笉鐩哥瓑锛屽垯浜х敓涓�涓敊璇紝浣嗕笉杩斿洖锛屽洜涓鸿鍑芥暟鏄┖杩斿洖鍊煎嚱鏁帮紝濡傛灉瀹為檯鍙傛暟涓暟灏忎簬褰㈠弬鍒楄〃闀垮害锛岄偅涔堝浣欑殑褰㈠弬
			 * 灏嗕細琚帇鍏il鍘熷瓙锛屽鏋滃疄闄呭弬鏁颁釜鏁板浣欏舰鍙傚垪琛ㄩ暱搴﹀垯澶氫綑鐨勫疄鍙備笉浣滃鐞�
			 */
			if(this.args.getLength() > args.length) {//褰㈠弬鍒楄〃闀垮害澶т簬瀹為檯鍙傛暟鏁扮粍鐨勯暱搴�
				new Error("a customize function expected more args or it will be transformed Atom \"nil\"");//姝ゅ浜х敓涓�涓敊璇絾涓嶈繑鍥烇紝绋嬪簭浼氱户缁繍琛岋紝浣嗗湪缁堢浼氭湁鐩稿簲鎻愮ず
				for(int i = 0;i < this.args.getLength();i++) {//渚濇杩唬姣忎釜鍙傛暟鍒楄〃鐨勭储寮曞��
					if(i >= args.length) {//濡傛灉褰撳墠绱㈠紩鍊煎ぇ浜庢垨绛変簬鏁扮粍鐨勯暱搴︼紝鍒欐剰鍛崇潃姝ゆ椂闇�瑕佸皢褰㈠弬鍒楄〃涓殑鍘熷瓙鍘嬪叆鍘熷瓙nil
						/*
						 * 榛樿鍙傛暟鍒楄〃涓墍鏈夊厓绱犵殑鏁版嵁绫诲瀷閮芥槸鍘熷瓙锛岃繖涓潯浠惰鍦ㄦ瀯閫犱竴涓嚱鏁版椂灏辫婊¤冻
						 */
						((Atom)(this.args.getItemById(i).data)).setValue(new JLispData(symbolpool.getSymbolByString("nil"), JLispType.ATOM));
					}else {
						((Atom)(this.args.getItemById(i).data)).setValue(args[i]);
						
					}
				}
			}else {//褰㈠弬鍒楄〃闀垮害灏忎簬鎴栫瓑浜庡疄闄呭弬鏁版暟缁勭殑闀垮害
				for(int i = 0;i < this.args.getLength();i++) {//渚濇杩唬姣忎釜鍙傛暟鍒楄〃鐨勭储寮曞��
					
					((Atom)(this.args.getItemById(i).data)).setValue(args[i]);
				}
			}
		}else {//濡傛灉鍙傛暟鍒楄〃涓師瀛�&rest鐨勭储寮曞�间笉涓�-1鐨勮瘽锛岄偅涔堜唬琛ㄥ弬鏁板垪琛ㄤ腑鏈�&rest鍘熷瓙锛屽苟涓斿畠鐨勪綅缃竴瀹氬浜庡弬鏁板垪琛ㄧ殑鍊掓暟绗簩涓綅缃笂锛岃繖涓�鐐瑰悓鏍烽渶瑕佸湪鏋勯�犺鍑芥暟鏃朵繚璇�
			if(this.args.getLength() - args.length > 1) {//濡傛灉褰㈠弬鍒楄〃鐨勯暱搴︽瘮瀹為檯鍙傛暟鏁扮粍鐨勯暱搴﹀涓や釜鎴栦袱涓互涓�
				for(int i = 0;i < this.args.getLength();i++) {
					if(i >= args.length) {
						if(i == this.args.getLength() - 2) {//姝ゆ椂鐨勭储寮曞�煎垰濂芥槸&rest鍘熷瓙鐨勭储寮曞�硷紝涓嶅仛澶勭悊缁х画涓嬩竴灞傚惊鐜�
							continue;
						}else {
							((Atom)(this.args.getItemById(i).data)).setValue(new JLispData(symbolpool.getSymbolByString("nil"), JLispType.ATOM));
						}
					}else {
						((Atom)(this.args.getItemById(i).data)).setValue(args[i]);
					}
				}
			}else {
				/*
				 * 鍒涘缓涓�涓暟缁勭敤鏉ユ瀯寤哄垪琛�
				 */
				JLispData[] endOfArgs = new JLispData[args.length - this.args.getLength() + 2];
				for(int i = 0;i < this.args.getLength() - 2;i++) {
					((Atom)(this.args.getItemById(i).data)).setValue(args[i]);
				}
				for(int i = this.args.getLength() - 2,j = 0;j < endOfArgs.length;i++,j++) {
					endOfArgs[j] = args[i];
				}
				((Atom)(this.args.getItemById(this.args.getLength() - 1).data)).setValue(new JLispData(new Cons(endOfArgs, symbolpool.getSymbolByString("nil")), JLispType.CONS));
			}	
		}
	}
	private void popValuesFromArgs(SymbolPool symbolpool) {
		//if(this.args.indexOfAtom(symbolpool.getSymbolByString("&rest")) == -1) {//鍗充唬琛ㄥ弬鏁板垪琛ㄤ腑娌℃湁&rest鍘熷瓙
			for(int i = 0;i < this.args.getLength();i++) {
				if("&rest".equalsIgnoreCase(((Atom)(this.args.getItemById(i).data)).name)) {
					continue;
				}
				((Atom)(this.args.getItemById(i).data)).pop();
			}
		//}
	}
	
	public JLispData eval() {//瀹忕殑杩斿洖鍊兼槸鍏惰嚜韬�,涓嶉渶瑕佺鍙锋睜
		return new JLispData(this, JLispType.MACRO);
	}
	
	public boolean isAlive() {//鑷鏈哄埗锛屽綋涓嶄綔涓轰换浣曚竴涓鍙风殑涓�鑸�兼垨鍑芥暟鍊兼椂琚涓烘槸鈥滈攢姣佲�濈殑锛岄渶瑕佷粠鍑芥暟姹犱腑绉婚櫎
		
		if(null == this.asSymbolFunction) {
			return false;
		}else if(this.asSymbolFunction.size() == 0) {
			return false;
		}else {
			return true;
		}
		
	}
}
